;----------------------------------------------------------------------------
;File name:	NET_TCON.SH			Revision date:	1997.08.04
;Creator:	Ulf Ronald Andersson		Creation date:	1997.07.31
;(c)1997 by:	Ulf Ronald Andersson		All rights reserved
;----------------------------------------------------------------------------
;Required header declarations:
;
;	.include	"uran\STRUCT.SH"
;	.include	"uran\URAn_SYS.SH"
;	.include	"sting\TRANSPRT.SH"
;	.include	"sting\DOMAIN.SH"
;	.include	"sting\NET_TCON.SH"
;
;copy the above to the header of your program and 'uncomment' the includes
;----------------------------------------------------------------------------
;Purpose:
;
;The macros declared here will handle the translation of time and date values
;between three formats:  The internal 2*16 bit code of TOS, the normal 32 bit
;network time format of internet, and the normal human format where each time
;unit is separate from the others.
;----------------------------------------------------------------------------
;NB: Having used any of these macros, you have to include a call like this:
;
;		make	TCON_links
;
;    This must be somewhere in the executable code, after all 'tcon_' macros
;    that are to be used in the program have been called at least once.
;----------------------------------------------------------------------------
;Available macros and their usage:
;
;
;tcon_real2tos	The TOS time of a tcon struct is loaded from TOS realtime
;tcon_tos2real	TOS realtime is loaded from the TOS time of a tcon struct
;tcon_man2tos	Human time of a tcon struct is converted into its TOS time
;tcon_tos2man	TOS time of a tcon struct is converted into its human time
;tcon_man2net	Human time of a tcon struct is converted into network time
;tcon_net2man	Network time of a tcon struct is converted into human time
;tcon_is_summer	Flags summertime with -1 and non-summer with 0
;tcon_rd_zone	Converts data from sting variable TIME_ZONE to zoneseconds
;tcon_rd_summer	Converts data from sting variable TIME_SUMMER to tcon_summer
;
;All of these macros expect a pointer to a 'tcon' structure in a0 and they
;restore all CPU address and data registers to original values on exit.
;
; NB:	use "ds.b    sizeof_tcon" to reserve storage for a tcon structure.
;
;+NB:	TIME_ZONE has a range of "-1440" to "+1440" in minutes ahead of GMT
;		  so for Germany or Sweden (for example) "+60" is correct.
;	tcon_zoneseconds has a range of -86400 to +86400 in seconds, which
;		  corresponds to TIME_ZONE multiplied by 60
;----------------------------------------------------------------------------
;The tcon structure:
;
	struct	tcon
	uint32	tcon_net_time		;network time from 00:00 Jan 1 1900 GMT
	int32	tcon_zoneseconds	;max +/- 45000  (== 12.5 hours)
	uint16	tcon_tos_date		;bitpacked date of Atari TOS
	uint16	tcon_tos_time		;bitpacked time of Atari TOS
	uint16	tcon_man_year		;human year
	uint16	tcon_man_month		;human month
	uint16	tcon_man_date		;human date
	uint16	tcon_man_hour		;human hour
	uint16	tcon_man_minute		;human minute
	uint16	tcon_man_second		;human second
	uint16	tcon_man_weekday	;human weekday
	d_alias	tcon_summer		;sub_struct for daylight savings
	uint8	tcon_summer_start_month	;\
	uint8	tcon_summer_start_date	; \/ In this period another hour
	uint8	tcon_summer_end_month	; /\ is added to the zone time
	uint8	tcon_summer_end_date	;/   (Zero values disable this)
	d_end	tcon
;
;NB:	weekday support is limited to the conversion tcon_net2man.
;	For tos and human formats that need weekday updated, you must
;	first convert to network format, and then to human format.
;	This is not needed unless weekday is necessary.
;
;	examples of conversions making tcon_weekday valid:
;
;	network	format	=> tcon_net2man
;	human format	=> tcon_man2net + tcon_net2man
;	tos format	=> tcon_tos2man + tcon_man2net + tcon_net2man
;
;----------------------------------------------------------------------------
;
NET_JAN_1980	equ	2524521600	;1980.01.01 was a tuesday :-)
NET_DAY		equ	86400
;
;----------------------------------------------------------------------------
;
.MACRO	TCON_links
	_unidef	tcon_real2tos
	_unidef	tcon_tos2real
	_unidef	tcon_man2tos
	_unidef	tcon_tos2man
	_unidef	tcon_man2net
	_unidef	tcon_net2man
	_unidef	tcon_rd_zone
	_unidef	tcon_rd_summer
	_unidef	tcon_is_summer
.ENDM	TCON_links
;
	_unidec	tcon_real2tos
	_unidec	tcon_tos2real
	_unidec	tcon_man2tos
	_unidec	tcon_tos2man
	_unidec	tcon_man2net
	_unidec	tcon_net2man
	_unidec	tcon_rd_zone
	_unidec	tcon_rd_summer
	_unidec	tcon_is_summer
;
;-------------------------------------
;
.MACRO	tcon_real2tos
	_uniref	tcon_real2tos
	jsr	code_tcon_real2tos
.ENDM	tcon_real2tos
;
.MACRO	code_tcon_real2tos
	movem.l	d0-d4/a0-a3,-(sp)
	move.l	a0,a3		;a3 -> tcon structure
	gemdos	Tgetdate
	move	d0,d3
	gemdos	Tgettime
	move	d0,d4
	gemdos	Tgetdate
	cmp	d0,d3
	beq.s	.have_time
	move	d0,d3
	gemdos	Tgettime
	move	d0,d4
.have_time:
	move	d3,tcon_tos_date(a3)
	move	d4,tcon_tos_time(a3)
	movem.l	(sp)+,d0-d4/a0-a3
	rts
.ENDM	code_tcon_real2tos
;
;-------------------------------------
;
.MACRO	tcon_tos2real
	_uniref	tcon_tos2real
	jsr	code_tcon_tos2real
.ENDM	tcon_tos2real
;
.MACRO	code_tcon_tos2real
	movem.l	d0-d2/a0-a3,-(sp)
	move.l	a0,a3		;a3 -> tcon structure
	gemdos	Tsettime,tcon_tos_time(a3)
	gemdos	Tsetdate,tcon_tos_date(a3)
	movem.l	(sp)+,d0-d2/a0-a3
	rts
.ENDM	code_tcon_tos2real
;
;-------------------------------------
;
.MACRO	tcon_tos2man
	_uniref	tcon_tos2man
	jsr	code_tcon_tos2man
.ENDM	tcon_tos2man
;
.MACRO	code_tcon_tos2man
;Sub converts TOS time to human time
	movem.l	d0/d1,-(sp)
	move	tcon_tos_date(a0),d0
	move	d0,d1
	and	#1<<5-1,d1
	move	d1,tcon_man_date(a0)
	lsr	#5,d0
	move	d0,d1
	and	#1<<4-1,d1
	move	d1,tcon_man_month(a0)
	lsr	#4,d0
	add	#1980,d0
	move	d0,tcon_man_year(a0)
;
	move	tcon_tos_time(a0),d0
	move	d0,d1
	and	#1<<5-1,d1
	add	d1,d1
	move	d1,tcon_man_second(a0)
	lsr	#5,d0
	move	d0,d1
	and	#1<<6-1,d1
	move	d1,tcon_man_minute(a0)
	lsr	#6,d0
	move	d0,tcon_man_hour(a0)
	movem.l	(sp)+,d0/d1
	rts
.ENDM	code_tcon_tos2man
;
;-------------------------------------
;
.MACRO	tcon_man2tos
	_uniref	tcon_man2tos
	jsr	code_tcon_man2tos
.ENDM	tcon_man2tos
;
.MACRO	code_tcon_man2tos
;Sub converts human time to TOS time
	movem.l	d0/d1,-(sp)
	move	tcon_man_year(a0),d1
	sub	#1980,d1
	asl	#4,d1
	move	tcon_man_month(a0),d0
	and	#1<<4-1,d0
	or	d0,d1
	asl	#5,d1
	move	tcon_man_date(a0),d0
	and	#1<<5-1,d0
	or	d0,d1
	move	d1,tcon_tos_date(a0)
;
	move	tcon_man_hour(a0),d1
	asl	#6,d1
	move	tcon_man_minute(a0),d0
	and	#1<<6-1,d0
	or	d0,d1
	asl	#5,d1
	move	tcon_man_second(a0),d0
	lsr	#1,d0
	and	#1<<5-1,d0
	or	d0,d1
	move	d1,tcon_tos_time(a0)
	movem.l	(sp)+,d0/d1
	rts
.ENDM	code_tcon_man2tos
;
;-------------------------------------
;
.MACRO	tcon_net2man
	_uniref		tcon_net2man
	jsr		code_tcon_net2man
.ENDM	tcon_net2man
;
.MACRO	code_tcon_net2man
;Sub converts network time to local human time
	movem.l		d0-d3,-(sp)
	clr.l		d3		;zero summer adjustment for first lap
.adjust_loop:
	move.l		tcon_net_time(a0),d0
	add.l		tcon_zoneseconds(a0),d0
	add.l		d3,d0		;add summer adjustment (on second lap when used)
	sub.l		#NET_JAN_1980,d0
	clr		d1		;prep for seconds mixing
	lsr.l		#1,d0		;prescale to avoid overflow in remainder
	roxr		d1		;d1 bit 15 = odd seconds bit
	divu		#NET_DAY/2,d0
	move		d0,d2		;d2 = whole days from 00:00 tuesday Jan 1st 1980
	clr		d0		;prep to separate remainder
	swap		d0
	divu		#60/2,d0	;d0.lo = minutes from midnight
	swap		d0
	move.b		d0,d1		;d1 = all seconds bits rotated 1 step right
	clr		d0		;prep to separate hours & minutes
	swap		d0
	divu		#60,d0
	move		d0,tcon_man_hour(a0)
	swap		d0
	move		d0,tcon_man_minute(a0)
	rol		#1,d1
	move		d1,tcon_man_second(a0)	;this completes time_of_day conversion
;
	clr.l		d0
	move		d2,d0		;d0 = d2 = days from Jan 1st 1980
	divu		#4*365+1,d0	;olympic periods since 1980
	asl		#2,d0		;are precisely 4 years each (until 2100)
	move		d0,d1		;d1 = years that formed olympics
	clr		d0		;prep to separate leap year
	swap		d0		;d0 = remaining days in last olympic
	cmp		#366,d0		;first year in olympic is leap year
	bhs.s		.more_years
	subq		#1,d0		;adjust index for post-leap_day (most common)
	cmp		#58,d0		;beyond February ?
	bgt.s		.done_years	;then it is now normalized
	beq.s		.leap_day	;leap day is treated separately
	addq		#1,d0		;restore original index for Jan 1..Feb 28
	bra.s		.done_years	;then treat it as any other year
;
.leap_day:
	move		#365,d0		;beyond the table for days in normal years
	bra.s		.done_years
	
;
.more_years:
	subq		#1,d0		;remove leap day from days in d0
	divu		#365,d0		;d0.lo = full years in last olympic
	add		d0,d1		;d1 = total years since 1980
	swap		d0		;d0 = day_in_year
.done_years:
	add		#1980,d1	;d1 = real year
	move		d1,tcon_man_year(a0)
	add		d0,d0			;prep to index 16 bit words
	move		.day_table(pc,d0.w),d0	;d0 = month<<8 + date
	clr		d1
	move.b		d0,d1
	lsr		#8,d0
	move		d0,tcon_man_month(a0)
	move		d1,tcon_man_date(a0)
	tst		d3			;summer adjustment already done ?
	bne.s		.finish_net2man
	move.l		#3600,d3		;prep to adjust by adding one hour
	tcon_is_summer
	bne		.adjust_loop
.finish_net2man:
	addq		#2,d2		;adjust weekday offset, so we get sunday=0
	divu		#7,d2
	swap		d2		;d2 = weekday offset 0=sunday .. 6= saturday
	move		d2,tcon_man_weekday(a0)
	movem.l		(sp)+,d0-d3
	rts
;
.day_table:
	dc.w	$101,$102,$103,$104,$105,$106,$107,$108,$109,$10A	;January
	dc.w	$10B,$10C,$10D,$10E,$10F,$110,$111,$112,$113,$114
	dc.w	$115,$116,$117,$118,$119,$11A,$11B,$11C,$11D,$11E,$11F
;
	dc.w	$201,$202,$203,$204,$205,$206,$207,$208,$209,$20A	;February
	dc.w	$20B,$20C,$20D,$20E,$20F,$210,$211,$212,$213,$214
	dc.w	$215,$216,$217,$218,$219,$21A,$21B,$21C
;
	dc.w	$301,$302,$303,$304,$305,$306,$307,$308,$309,$30A	;March
	dc.w	$30B,$30C,$30D,$30E,$30F,$310,$311,$312,$313,$314
	dc.w	$315,$316,$317,$318,$319,$31A,$31B,$31C,$31D,$31E,$31F
;
	dc.w	$401,$402,$403,$404,$405,$406,$407,$408,$409,$40A	;April
	dc.w	$40B,$40C,$40D,$40E,$40F,$410,$411,$412,$413,$414
	dc.w	$415,$416,$417,$418,$419,$41A,$41B,$41C,$41D,$41E
;
	dc.w	$501,$502,$503,$504,$505,$506,$507,$508,$509,$50A	;May
	dc.w	$50B,$50C,$50D,$50E,$50F,$510,$511,$512,$513,$514
	dc.w	$515,$516,$517,$518,$519,$51A,$51B,$51C,$51D,$51E,$51F
;
	dc.w	$601,$602,$603,$604,$605,$606,$607,$608,$609,$60A	;June
	dc.w	$60B,$60C,$60D,$60E,$60F,$610,$611,$612,$613,$614
	dc.w	$615,$616,$617,$618,$619,$61A,$61B,$61C,$61D,$61E
;
	dc.w	$701,$702,$703,$704,$705,$706,$707,$708,$709,$70A	;July
	dc.w	$70B,$70C,$70D,$70E,$70F,$710,$711,$712,$713,$714
	dc.w	$715,$716,$717,$718,$719,$71A,$71B,$71C,$71D,$71E,$71F
;
	dc.w	$801,$802,$803,$804,$805,$806,$807,$808,$809,$80A	;August
	dc.w	$80B,$80C,$80D,$80E,$80F,$810,$811,$812,$813,$814
	dc.w	$815,$816,$817,$818,$819,$81A,$81B,$81C,$81D,$81E,$81F
;
	dc.w	$901,$902,$903,$904,$905,$906,$907,$908,$909,$90A	;September
	dc.w	$90B,$90C,$90D,$90E,$90F,$910,$911,$912,$913,$914
	dc.w	$915,$916,$917,$918,$919,$91A,$91B,$91C,$91D,$91E
;
	dc.w	$A01,$A02,$A03,$A04,$A05,$A06,$A07,$A08,$A09,$A0A	;October
	dc.w	$A0B,$A0C,$A0D,$A0E,$A0F,$A10,$A11,$A12,$A13,$A14
	dc.w	$A15,$A16,$A17,$A18,$A19,$A1A,$A1B,$A1C,$A1D,$A1E,$A1F
;
	dc.w	$B01,$B02,$B03,$B04,$B05,$B06,$B07,$B08,$B09,$B0A	;November
	dc.w	$B0B,$B0C,$B0D,$B0E,$B0F,$B10,$B11,$B12,$B13,$B14
	dc.w	$B15,$B16,$B17,$B18,$B19,$B1A,$B1B,$B1C,$B1D,$B1E
;
	dc.w	$C01,$C02,$C03,$C04,$C05,$C06,$C07,$C08,$C09,$C0A	;December
	dc.w	$C0B,$C0C,$C0D,$C0E,$C0F,$C10,$C11,$C12,$C13,$C14
	dc.w	$C15,$C16,$C17,$C18,$C19,$C1A,$C1B,$C1C,$C1D,$C1E,$C1F
;
	dc.w	$21D	;special entry for leap day
.ENDM	code_tcon_net2man
;
;-------------------------------------
;
.MACRO	tcon_man2net
	_uniref	tcon_man2net
	jsr	code_tcon_man2net
.ENDM	tcon_man2net
;
.MACRO	code_tcon_man2net
;Sub converts local human time to network time
	movem.l	d0-d3,-(sp)
	tcon_is_summer
	move.l	#3600,d3
	and	d0,d3
	move	tcon_man_year(a0),d0
	sub	#1980,d0	;make year relative to 1980
	move	d0,d1
	lsr	#2,d0		;d0 = olympics
	mulu	#365*4+1,d0	;d0 = days of full olympics
	and	#1<<2-1,d1	;d1 = years of current olympic
	seq	d2		;d2.b=$FF if current year is leaping, else $00
	beq.s	.done_years
	mulu	#365,d1		;d1 = normal days of full years of current olympic
	addq	#1,d1		;add a leap day for the first of them
	add	d1,d0		;d0 = days of all full years  (NB: <65536 !!!)
;	
.done_years:
	and	#12,d2		;d2 = table offset for leap year, or zero
	add	tcon_man_month(a0),d2	;d2 = month index (0-23, with 12-23 for leap years)
	subq	#1,d2		;subtract one since months don't use zero
	add	d2,d2				;prep to index 16 bit words
	add	.month_table(pc,d2.w),d0	;add days before current month to d0
	add	tcon_man_date(a0),d0			;add date in current month to d0
	subq	#1,d0		;subtract one, since dates don't use zero
	mulu	#NET_DAY/2,d0
	add.l	d0,d0		;d0 = date in seconds relative to Jan 1 1980
	add.l	#NET_JAN_1980,d0	;d0.l = local date (but not yet time) in seconds
	move	tcon_man_hour(a0),d1
	mulu	#60,d1
	add	tcon_man_minute(a0),d1
	mulu	#60/2,d1	;NB: the "/2" is needed to avoid overflow
	add.l	d1,d1		;This doubling compensates for the above "/2"
	clr.l	d2
	move	tcon_man_second(a0),d2
	add.l	d2,d1		;d1.l = local time_of_day in seconds
	add.l	d1,d0		;d0.l = local time in network format
	sub.l	d3,d0		;adjust for daylight savings
	sub.l	tcon_zoneseconds(a0),d0	;d0.l = true network time
	move.l	d0,tcon_net_time(a0)
	movem.l	(sp)+,d0-d3
	rts
;
.month_table:
	dc.w	0,31,59,90,120,151,181,212,243,273,304,334
	dc.w	0,31,60,91,121,152,182,213,244,274,305,335
.ENDM	code_tcon_man2net
;
;-------------------------------------
;
.MACRO	tcon_is_summer
	_uniref	tcon_is_summer
	jsr	code_tcon_is_summer
.ENDM	tcon_is_summer
;
.MACRO	code_tcon_is_summer
	move.l	d1,-(sp)
	tst.l	tcon_summer(a0)
	beq.s	.exit_false
	clr.l	d0
	move	tcon_man_month(a0),d0
	add	#12,d0
	sub.b	tcon_summer_start_month(a0),d0
	divu	#12,d0
	swap	d0
	tst	d0	;start month now ?
	bne.s	.not_start_month
;start month is now
	move	tcon_man_date(a0),d0
	cmp.b	tcon_summer_start_date(a0),d0
	blo.s	.exit_false
.exit_true:
	move.l	(sp)+,d1
	moveq	#-1,d0
	rts
;
.not_start_month:
	clr.l	d1
	move.b	tcon_summer_end_month(a0),d1
	add	#12,d1
	sub.b	tcon_summer_start_month(a0),d1
	divu	#12,d1
	swap	d1
	cmp	d1,d0
	bhi.s	.exit_false
	blo.s	.exit_true
;end month is now
	move	tcon_man_date(a0),d0
	cmp.b	tcon_summer_end_date(a0),d0
	blo.s	.exit_true
.exit_false:
	move.l	(sp)+,d1
	clr.l	d0
	rts
.ENDM	code_tcon_is_summer
;
;-------------------------------------
;
.MACRO	tcon_rd_zone
	_uniref	tcon_rd_zone
	jsr	code_tcon_rd_zone
.ENDM	tcon_rd_zone
;
.MACRO	code_tcon_rd_zone
;Sub converts config variable TIME_ZONE to tcon_zoneseconds
	movem.l	d0-d2/a0-a2,-(sp)
	move.l		a0,-(sp)
	getvstr		TIME_ZONE_vn_s(pc)
	is_unblank.i	d0
	clr		d1
	clr.l		d2
	move.l		a0,d0
	beq.s		.have_zone
	move.b		(a0)+,d1
	cmp.b		#'+',d1
	beq.s		.use_sign
	cmp.b		#'-',d1
	beq.s		.use_sign
	subq		#1,a0
.use_sign:
	clr		d0
.loop:
	move.b		(a0)+,d0
	sub.b		#'0',d0
	blt.s		.have_zone
	cmp.b		#9,d0
	bhi.s		.have_zone
	mulu		#10,d2
	add		d0,d2
	cmp		#1440,d2	;too high ?
	bls.s		.loop		;loop for legal values
	clr.l		d2		;force illegal value to zero
.have_zone:
	mulu		#60,d2		;convert to seconds
	cmp.b		#'-',d1
	bne.s		.keep_sign
	neg.l		d2		;adjust to negative sign
.keep_sign:
	move.l		(sp)+,a0
	move.l		d2,tcon_zoneseconds(a0)
	movem.l		(sp)+,d0-d2/a0-a2
	rts
;
TIME_ZONE_vn_s:
	dc.b	'TIME_ZONE',NUL
	even
.ENDM	code_tcon_rd_zone
;
;-------------------------------------
;
.MACRO	tcon_rd_summer
	_uniref	tcon_rd_summer
	jsr	code_tcon_rd_summer
.ENDM	tcon_rd_summer
;
.MACRO	code_tcon_rd_summer
;Sub converts config variable TIME_ZONE to tcon_zoneseconds
	movem.l		d0-d2/a0-a2,-(sp)
	move.l		a0,-(sp)
	getvstr		TIME_SUMMER_vn_s(pc)
	is_unblank.i	d0
	move.l		a0,d0
	beq.s		.have_summer
	diptobip	(a0)
.have_summer:
	move.l		(sp)+,a0
	move.l		d0,tcon_summer(a0)
	movem.l		(sp)+,d0-d2/a0-a2
	rts
;
TIME_SUMMER_vn_s:
	dc.b		'TIME_SUMMER',NUL
	even
.ENDM	code_tcon_rd_summer
;
;----------------------------------------------------------------------------
;End of file:	NET_TCON.SH
;----------------------------------------------------------------------------
